/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/gfx/PolylineShape3DImpl.h"
#include "alkes/core/Memory.h"

namespace alkes {

PolylineShape3DImpl* PolylineShape3DImpl::create(IGraphics3D* graphics, uint32_t num)
{
    AL_PRE_COND(graphics && num, 0);

    return new PolylineShape3DImpl(graphics, num);
}

void PolylineShape3DImpl::destroy()
{
    delete this;
}

PolylineShape3DImpl::PolylineShape3DImpl(IGraphics3D* graphics, uint32_t num)
: PolylineShape(num)
, graphics_(graphics)
, polylines_(0)
{
    uint32_t i;

    polylines_ = (intrusive_ptr<IVertexBuffer>*)alkes_malloc(sizeof(intrusive_ptr<IVertexBuffer>) * num);
    for (i = 0; i < num; ++i)
    {
        new(polylines_ + i) intrusive_ptr<IVertexBuffer>(0);
    }
}

PolylineShape3DImpl::~PolylineShape3DImpl()
{
    if (polylines_)
    {
        uint32_t i;
        for (i = 0; i < getArrayNum(); ++i)
        {
            (polylines_ + i)->~intrusive_ptr<IVertexBuffer>();
        }
        alkes_free(polylines_);
    }
}

IVertexBuffer* PolylineShape3DImpl::getVertexBuffer(uint32_t index)
{
    if (!polylines_[index])
        polylines_[index] = graphics_->createVertexBuffer(AL_VERTEX_XY|AL_VERTEX_COLOR);

    return polylines_[index];
}

bool PolylineShape3DImpl::renderTo(IRenderTarget3D* target, uint32_t start, uint32_t count)
{
    if (count == 0)
        return true;

    AL_PRE_COND(validate(start) && validate(start + count - 1) && construct(target), false);

    uint32_t i;
    for (i = start; i < start + count; ++i)
    {
        const Polyline& polyline = getPolyline(i);
        if (polyline.count)
        {
            IVertexBuffer* vbuffer = polylines_[i];
            if (vbuffer && vbuffer->getVertexCount())
            {
                target->drawPrimitive(vbuffer, AL_PRIM_LINE_STRIP, 0, vbuffer->getVertexCount());
            }
        }
    }

    return true;
}

bool PolylineShape3DImpl::construct(IRenderTarget3D* /*target*/)
{
    if (!isDirty())
        return true;

    uint32_t i;
    for (i = 0; i < getArrayNum(); ++i)
    {
        const Polyline& polyline = getPolyline(i);
        if (polyline.count)
        {
            setVertex(getVertexBuffer(i), i, polyline);
        }
    }

    setDirty(false);

    return true;
}

void PolylineShape3DImpl::setVertex(IVertexBuffer* vbuffer, uint32_t index, const Polyline& polyline)
{
    AL_PRE_COND(validate(index) && 2 <= polyline.count && vbuffer, );

    const Point2D* points = polyline.points;
    uint32_t i;
    Vector2D vertex[4];
    Color colors[4] = {polyline.color, polyline.color, polyline.color, polyline.color};
    for (i = 0; i < polyline.count / 4; ++i)
    {
        vertex[0].x = points[i*4  ].x; vertex[0].y = points[i*4  ].y;
        vertex[1].x = points[i*4+1].x; vertex[1].y = points[i*4+1].y;
        vertex[2].x = points[i*4+2].x; vertex[2].y = points[i*4+2].y;
        vertex[3].x = points[i*4+3].x; vertex[3].y = points[i*4+3].y;
        vbuffer->setVertex2DDiffuse(i * 4, 4, vertex, colors);
    }

    if (polyline.count & 3)
    {
        points += polyline.count / 4;
        for (i = 0; i < (polyline.count & 3); ++i)
        {
            vertex[i].x = points->x;
            vertex[i].y = points->y;
            ++points;
        }
        vbuffer->setVertex2DDiffuse(polyline.count & (~3), polyline.count % 4, vertex, colors);
    }
}

}
